﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using DomainDriver.DomainModeling.Constraints;
using DomainDriver.DomainModeling.DataProviders;
using DomainDriver.DomainModeling.DomainModels;
using DomainDriver.DomainModeling.DomainObjects;
using DomainDriver.DomainModeling.Exceptions;
using DomainDriver.DomainModeling.Repositories;

namespace DomainDriver.DomainModeling.GenericUnitTesting.Constraints
{
    public abstract class ListRelationConstraintTestFixture<DOMAIN_MODEL, OWNER_KEY, ELEMENT_KEY, OWNER, ELEMENT> : ConstraintTestFixture<DOMAIN_MODEL, ListRelationConstraint<OWNER_KEY, ELEMENT_KEY, OWNER, ELEMENT>>
        where DOMAIN_MODEL : class, IDomainModel
        where OWNER : IKeyedDomainObject<OWNER_KEY, OWNER>
        where ELEMENT : IKeyedDomainObject<ELEMENT_KEY, ELEMENT>
    {
        public ListRelationConstraintTestFixture(DataSourcePersistenceType persistenceType)
            : base(persistenceType)
        {
            // do nothing
        }

        protected virtual IRepository<ELEMENT_KEY, ELEMENT> ElementRepository
        {
            get { return DomainModel.GetRepository<ELEMENT_KEY, ELEMENT>(); }
        }

        protected virtual IRepository<OWNER_KEY, OWNER> OwnerRepository
        {
            get { return DomainModel.GetRepository<OWNER_KEY, OWNER>(); }
        }

        protected abstract OWNER CreateOwner();
        protected abstract ELEMENT CreateElement();
        protected abstract void AddToList(OWNER owner, ELEMENT_KEY elementKey);
        protected abstract void RemoveFromList(OWNER owner, ELEMENT_KEY elementKey);
        protected abstract void ClearList(OWNER owner);
        protected abstract int ListCount(OWNER owner);
        protected abstract bool ListContains(OWNER owner, ELEMENT_KEY elementKey);

        [Test]
        public virtual void VerifyConstraintOnAddingOwner_WITHOUT_E_POSITIVE()
        {
            OWNER owner = CreateOwner();

            OwnerRepository.Add(owner);

            SaveAndReloadDomainModel();

            owner = OwnerRepository.Read(owner.Key);
            Assert.AreEqual(0, ListCount(owner));
        }

        [Test]
        public virtual void VerifyConstraintOnAddingOwner_WITH_E_POSITIVE()
        {
            ELEMENT element = CreateElement();
            OWNER owner = CreateOwner();
            OWNER ownerCopy = default(OWNER);

            ownerCopy = owner.Copy();
            AddToList(owner, element.Key);
            Assert.AreNotEqual(ownerCopy, owner);

            ElementRepository.Add(element);
            OwnerRepository.Add(owner);

            SaveAndReloadDomainModel();

            owner = OwnerRepository.Read(owner.Key);
            Assert.AreEqual(1, ListCount(owner));
            Assert.IsTrue(ListContains(owner, element.Key));
        }

        [Test]
        [ExpectedException(typeof(DomainObjectManagementException))]
        public virtual void VerifyConstraintOnAddingOwner_WITH_E_NEGATIVE()
        {
            ELEMENT element = CreateElement();
            OWNER owner = CreateOwner();
            OWNER ownerCopy = default(OWNER);

            ownerCopy = owner.Copy();
            AddToList(owner, element.Key);
            Assert.AreNotEqual(ownerCopy, owner);

            // SKIP: ElementRepository.Add(element);
            OwnerRepository.Add(owner);
        }

        [Test]
        public virtual void VerifyConstraintOnUpdatingOwner_ADD_E_POSITIVE()
        {
            ELEMENT element = CreateElement();
            OWNER owner = CreateOwner();
            OWNER ownerCopy = default(OWNER);

            OwnerRepository.Add(owner);

            SaveAndReloadDomainModel();

            owner = OwnerRepository.Read(owner.Key);
            ownerCopy = owner.Copy();
            AddToList(owner, element.Key);
            Assert.AreNotEqual(ownerCopy, owner);

            ElementRepository.Add(element);
            OwnerRepository.Update(owner);

            SaveAndReloadDomainModel();

            owner = OwnerRepository.Read(owner.Key);
            Assert.IsTrue(ListContains(owner, element.Key));
        }

        [Test]
        [ExpectedException(typeof(DomainObjectManagementException))]
        public virtual void VerifyConstraintOnUpdatingOwner_ADD_E_NEGATIVE()
        {
            ELEMENT element = CreateElement();
            OWNER owner = CreateOwner();
            OWNER ownerCopy = default(OWNER);

            OwnerRepository.Add(owner);

            SaveAndReloadDomainModel();

            owner = OwnerRepository.Read(owner.Key);
            ownerCopy = owner.Copy();
            AddToList(owner, element.Key);
            Assert.AreNotEqual(ownerCopy, owner);

            // SKIP: ElementRepository.Add(element);
            OwnerRepository.Update(owner);
        }

        [Test]
        public virtual void VerifyConstraintOnUpdatingOwner_REMOVE_E_POSITIVE()
        {
            ELEMENT element = CreateElement();
            OWNER owner = CreateOwner();
            OWNER ownerCopy = default(OWNER);

            ownerCopy = owner.Copy();
            AddToList(owner, element.Key);
            Assert.AreNotEqual(ownerCopy, owner);

            ElementRepository.Add(element);
            OwnerRepository.Add(owner);

            SaveAndReloadDomainModel();

            owner = OwnerRepository.Read(owner.Key);
            ownerCopy = owner.Copy();
            RemoveFromList(owner, element.Key);
            Assert.AreNotEqual(ownerCopy, owner);

            ownerCopy = owner.Copy();
            OwnerRepository.Update(owner);

            SaveAndReloadDomainModel();

            owner = OwnerRepository.Read(owner.Key);
            Assert.AreEqual(ownerCopy, owner);
            Assert.IsFalse(ListContains(owner, element.Key));
        }

        [Test]
        public virtual void VerifyConstraintOnUpdatingOwner_SWITCH_E_POSITIVE()
        {
            ELEMENT element1 = CreateElement();
            ELEMENT element2 = CreateElement();
            OWNER owner = CreateOwner();
            OWNER ownerCopy = default(OWNER);

            ownerCopy = owner.Copy();
            AddToList(owner, element1.Key);
            Assert.AreNotEqual(ownerCopy, owner);

            ElementRepository.Add(element1);
            OwnerRepository.Add(owner);

            SaveAndReloadDomainModel();

            owner = OwnerRepository.Read(owner.Key);
            ownerCopy = owner.Copy();
            RemoveFromList(owner, element1.Key);
            Assert.AreNotEqual(ownerCopy, owner);

            ownerCopy = owner.Copy();
            AddToList(owner, element2.Key);
            Assert.AreNotEqual(ownerCopy, owner);

            ownerCopy = owner.Copy();
            ElementRepository.Add(element2);
            OwnerRepository.Update(owner);

            SaveAndReloadDomainModel();

            owner = OwnerRepository.Read(owner.Key);
            Assert.AreEqual(ownerCopy, owner);
            Assert.IsFalse(ListContains(owner, element1.Key));
            Assert.IsTrue(ListContains(owner, element2.Key));
        }

        [Test]
        [ExpectedException(typeof(DomainObjectManagementException))]
        public virtual void VerifyConstraintOnUpdatingOwner_SWITCH_E_NEGATIVE()
        {
            ELEMENT element1 = CreateElement();
            ELEMENT element2 = CreateElement();
            OWNER owner = CreateOwner();
            OWNER ownerCopy = default(OWNER);

            ownerCopy = owner.Copy();
            AddToList(owner, element1.Key);
            Assert.AreNotEqual(ownerCopy, owner);

            ElementRepository.Add(element1);
            OwnerRepository.Add(owner);

            SaveAndReloadDomainModel();

            owner = OwnerRepository.Read(owner.Key);
            ownerCopy = owner.Copy();
            RemoveFromList(owner, element1.Key);
            Assert.AreNotEqual(ownerCopy, owner);

            ownerCopy = owner.Copy();
            AddToList(owner, element2.Key);
            Assert.AreNotEqual(ownerCopy, owner);

            // SKIP: ElementRepository.Add(element2);
            OwnerRepository.Update(owner);
        }

        [Test]
        public virtual void VerifyConstraintOnDeletingElement_POSITIVE()
        {
            ELEMENT element = CreateElement();
            OWNER owner = CreateOwner();
            OWNER ownerCopy = default(OWNER);

            ownerCopy = owner.Copy();
            AddToList(owner, element.Key);
            Assert.AreNotEqual(ownerCopy, owner);

            ElementRepository.Add(element);
            OwnerRepository.Add(owner);

            SaveAndReloadDomainModel();

            OwnerRepository.Remove(owner.Key);
            ElementRepository.Remove(element.Key);

            SaveAndReloadDomainModel();

            Assert.IsFalse(OwnerRepository.Exists(owner.Key));
            Assert.IsFalse(ElementRepository.Exists(element.Key));
        }

        [Test]
        [ExpectedException(typeof(DomainObjectManagementException))]
        public virtual void VerifyConstraintOnDeletingElement_NEGATIVE()
        {
            ELEMENT element = CreateElement();
            OWNER owner = CreateOwner();
            OWNER ownerCopy = default(OWNER);

            ownerCopy = owner.Copy();
            AddToList(owner, element.Key);
            Assert.AreNotEqual(ownerCopy, owner);

            ElementRepository.Add(element);
            OwnerRepository.Add(owner);

            SaveAndReloadDomainModel();

            // SKIP: OwnerRepository.Remove(owner.Key);
            ElementRepository.Remove(element.Key);
        }
    }
}